//===--- FlagSet.h - Helper class for opaque flag types ---------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file defines the FlagSet template, a class which makes it easier to
// define opaque flag types.
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_BASIC_FLAGSET_H
#define SWIFT_BASIC_FLAGSET_H

#include <type_traits>
#include <assert.h>

namespace swift {

/// A template designed to simplify the task of defining a wrapper type
/// for a flags bitfield.
///
/// Unfortunately, this doesn't currently support functional-style
/// building patterns, which means this can't practically be used for
/// types that need to be used in constant expressions.
template <typename IntType>
class FlagSet {
	static_assert(std::is_integral<IntType>::value, "storage type for FlagSet must be an integral type");
	IntType Bits;

protected:
	template <unsigned BitWidth>
	static constexpr IntType lowMaskFor() {
		return IntType((1 << BitWidth) - 1);
	}

	template <unsigned FirstBit, unsigned BitWidth = 1>
	static constexpr IntType maskFor() {
		return lowMaskFor<BitWidth>() << FirstBit;
	}

	constexpr FlagSet(IntType bits = 0) : Bits(bits) {}

	/// Read a single-bit flag.
	template <unsigned Bit>
	bool getFlag() const {
		return Bits & maskFor<Bit>();
	}

	/// Set a single-bit flag.
	template <unsigned Bit>
	void setFlag(bool value) {
		if (value) {
			Bits |= maskFor<Bit>();
		} else {
			Bits &= ~maskFor<Bit>();
		}
	}

	/// Read a multi-bit field.
	template <unsigned FirstBit, unsigned BitWidth, typename FieldType = IntType>
	FieldType getField() const {
		return FieldType((Bits >> FirstBit) & lowMaskFor<BitWidth>());
	}

	/// Assign to a multi-bit field.
	template <unsigned FirstBit, unsigned BitWidth, typename FieldType = IntType>
	void setField(typename std::enable_if<true, FieldType>::type value) {
		// Note that we suppress template argument deduction for FieldType.
		assert(IntType(value) <= lowMaskFor<BitWidth>() && "value out of range");
		Bits = (Bits & ~maskFor<FirstBit, BitWidth>()) | (IntType(value) << FirstBit);
	}

	// A convenient macro for defining a getter and setter for a flag.
	// Intended to be used in the body of a subclass of FlagSet.
#define FLAGSET_DEFINE_FLAG_ACCESSORS(BIT, GETTER, SETTER)                                                             \
	bool GETTER() const { return this->template getFlag<BIT>(); }                                                      \
	void SETTER(bool value) { this->template setFlag<BIT>(value); }

	// A convenient macro for defining a getter and setter for a field.
	// Intended to be used in the body of a subclass of FlagSet.
#define FLAGSET_DEFINE_FIELD_ACCESSORS(BIT, WIDTH, TYPE, GETTER, SETTER)                                               \
	TYPE GETTER() const { return this->template getField<BIT, WIDTH, TYPE>(); }                                        \
	void SETTER(TYPE value) { this->template setField<BIT, WIDTH, TYPE>(value); }

	// A convenient macro to expose equality operators.
	// These can't be provided directly by FlagSet because that would allow
	// different flag sets to be compared if they happen to have the same
	// underlying type.
#define FLAGSET_DEFINE_EQUALITY(TYPENAME)                                                                              \
	friend bool operator==(TYPENAME lhs, TYPENAME rhs) { return lhs.getOpaqueValue() == rhs.getOpaqueValue(); }        \
	friend bool operator!=(TYPENAME lhs, TYPENAME rhs) { return lhs.getOpaqueValue() != rhs.getOpaqueValue(); }

public:
	/// Get the bits as an opaque integer value.
	IntType getOpaqueValue() const { return Bits; }
};

} // end namespace swift

#endif
